/*
 * Copyright 2010
 * Jairou Software Organization
 * All Rights Reserved.
 * 
 * This software is the proprietary 
 * information of Roger Ramia
 * and the Jairou Software Organization 
 * Use is subject to license terms.
 * 
 * For licensing information please visit
 * http://www.jairou.org/license
 * 
 * @author Roger Ramia
 */
package org.jairou.util;

import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.jairou.core.XML;


/**
 * Common utility providing core Email support
 * and uniform access to Email Servers
 * <br/>
 * This class sends pre-configured
 * email messages defined in xml (usually named email.xml).
 * <br/>
 * The primary function is:
 * 	Email.send() - which sends an xml configured email message
 * <br/>
 * This class also extends javax.mail.Authenticator 
 * and implements the getPasswordAuthentication method
 * to provide authentication support for email servers that require 
 * authentication
 * <br/>
 * Email host servers can be configured in jairou.xml
 * using the following xml configuration: 
 * <pre>{@code
 * 	<email-host id="yourId" authentication="true" ssl="true" port="465">
 *		<server>your.smtpserver.com</server>
 *		<username>support@yourdomain.com</username>
 *		<password>yourpassword</password>
 *	</email-host>
 * }</pre>
 * 
 * @author Roger Ramia
 */
public class Email {
	
	/**
	 * Simple utility method that sends an email using
	 * the default session obtained from the javamail api.
	 * and an Email messages must be defined in xml using the following format:
	 * <pre>{@code
	 * <email id="testMessage" host="myHostId" content="html">
	 *	<from>support@mydomain.com</from>
	 *	<to>${read('getSenderAddress')}</to>
	 *	<subject>Hello!</subject>
	 *	<body>
	 *		Hello ${firstName},
	 *		This is the body of my message.	
	 *      Thanks,	
	 *		${session.user.firstName}			
	 *	</body>
	 * </email>
	 * }</pre>
	 * 
	 * Note: Expressions will be resolved against the provided context,
	 *       Thus, a context with the property firstName set to 'Roger'
	 *       will be resolved when the email is sent.  Dot notation 
	 *       and function call expressions are also supported.
	 *       
	 * 
	 * @param host - id of the preconfigued email message defined in jairou.xml 
	 * @param ctx - current context object used to resolve the pre-defined 
	 * 				expressions embeded in the xml template
	 * 
	 * @throws Exception - All javamail exceptions related to connecting
	 * 					   to a host session and sending the message
	 */	 
	public static void send(String key, Object ctx)
	throws Exception {
		if (key != null) {
			String hostKey = (String) XML.resolveValue("email."+key+".host", ctx);
			Session session = getEmailSession(hostKey);
			
			String content = (String) XML.resolveValue("email."+key+".content", ctx);
			String from = (String) XML.resolveValue("email."+key+".from", ctx);
			String to = (String) XML.resolveValue("email."+key+".to", ctx);			
			String cc = (String) XML.resolveValue("email."+key+".cc", ctx);
			String bcc = (String) XML.resolveValue("email."+key+".bcc", ctx);
			String subject = (String) XML.resolveValue("email."+key+".subject", ctx);
			String body = (String) XML.resolveValue("email."+key+".body", ctx);

			if (session != null && from != null && to != null) {			

				Message message = new MimeMessage(session);
				message.setFrom(new InternetAddress(from));
			    message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
			    message.setSubject(subject);
			    
			    if (cc != null)
					message.addRecipients(Message.RecipientType.CC, InternetAddress.parse(cc));
				if (bcc != null)
					message.addRecipients(Message.RecipientType.BCC, InternetAddress.parse(bcc));

				// if content type is html wrap in html tags
				// otherwise set the content type to plain text
				if ("html".equalsIgnoreCase(content)) body = "<html><body>"+body+"</body></html>";
				else content = "plain";	

				MimeBodyPart bodypart = new MimeBodyPart();
				bodypart.setContent(body, "text/"+content);	
				Multipart mp = new MimeMultipart();
				mp.addBodyPart(bodypart);				
				message.setContent(mp);

				Transport.send(message);
			}
			else throw new MessagingException("unable to send email for id "+key+". Please ensure proper configuration in email xml");
		}
		else throw new MessagingException("no template found for id "+key+". Please ensure proper configuration in email xml");
	}
	
	/**
	 * returns an Email Host Session
	 * based on the host key defined in xml
	 * @param hostKey
	 * @return
	 */
	public static Session getEmailSession(String key)
	throws MessagingException {		
		String host = XML.get("email-host."+key+".server");
		String auth = XML.get("email-host."+key+".authentication");

		String ssl = XML.get("email-host."+key+".ssl");
		String port = XML.get("email-host."+key+".port");
		
		final String username = XML.get("email-host."+key+".username");
		final String password = XML.get("email-host."+key+".password");

		Properties props = new Properties();
		if (host != null && auth != null && port != null) {
	    	props.put("mail.smtp.host", host);
	    	if ("true".equals(ssl)) {
		    	props.put("mail.smtp.socketFactory.port", port);
		    	props.put("mail.smtp.socketFactory.class","javax.net.ssl.SSLSocketFactory");
	    	}
	    	props.put("mail.smtp.auth", auth);
	    	props.put("mail.smtp.port", port);
		}
		else throw new MessagingException("Unable to create session for host "+key+". Please ensure proper configuration of email-host");
		return Session.getDefaultInstance(props, new javax.mail.Authenticator() 
		{
			protected PasswordAuthentication getPasswordAuthentication()
			{ return new PasswordAuthentication(username, password); }
		});	
		
	}
}
